﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Cache;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Bing;

namespace MusicPlow
{
    public class ImageFetcher
    {
        const string c_bingAccountKey = "NeacFRnOrMb9rjENOc8l0zchm+lEDPzV5yHKW1g0vTI=";
        const string c_allCoversRequest = "http://www.allcdcovers.com/api/search/{0}/{1}/{2}";
        const string c_allCoversUser = "apiuser";
        const string c_allCoversPass = "dA3h346cEbuCAFra";

        static BingSearchContainer s_bingSearchContainer;

        static ImageFetcher()
        {
            s_bingSearchContainer = new BingSearchContainer(
              new Uri("https://api.datamarket.azure.com/Bing/Search/"));

            // the next line configures the bingContainer to use your credentials.
            s_bingSearchContainer.Credentials = new NetworkCredential(c_bingAccountKey, c_bingAccountKey);
        }

        #region public

        public static Task<TimedOperation<string>> GetImage(string imageSearchString)
        {
            DateTime startTime = DateTime.Now;
            return GetImageFromAllCovers(imageSearchString)
                .ContinueWith<TimedOperation<string>>(p =>
                    {
                        TimeSpan runTime = DateTime.Now - startTime;
                        if (string.IsNullOrEmpty(p.Result.Result))
                        {
                            var imageFromBingResult = GetImageFromBing(imageSearchString).Result;
                            return new TimedOperation<string>(imageFromBingResult.Result,
                                imageFromBingResult.RunTime.Add(runTime));
                        }
                        else
                        {
                            return p.Result;
                        }
                    });
        }

        #endregion

        #region private

        private static Task<TimedOperation<string>> GetImageFromBing(string imageSearchString)
        {
            // now we can build the query
            var imageQuery = s_bingSearchContainer.Image(imageSearchString, null, null, null, null, null);
            imageQuery.AddQueryOption("$top", "1");

            DateTime startTime = DateTime.Now;
            return Task.Factory.FromAsync<IEnumerable<ImageResult>>(
                imageQuery.BeginExecute,
                imageQuery.EndExecute,
                null).ContinueWith<TimedOperation<string>>(
                    p =>
                    {
                        TimeSpan runTime = DateTime.Now - startTime;
                        try
                        {
                            ImageResult firstRes = p.Result.FirstOrDefault();
                            return new TimedOperation<string>(firstRes.Thumbnail.MediaUrl, runTime);
                        }
                        catch
                        {
                            return new TimedOperation<string>(string.Empty, runTime);
                        }
                    });
        }

        private static Task<TimedOperation<string>> GetImageFromAllCovers(string imageSearchString)
        {            
            MD5CryptoServiceProvider md5Hash = new MD5CryptoServiceProvider();
                
            byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(c_allCoversPass, imageSearchString)));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            string md5String = sBuilder.ToString();

            DateTime startTime = DateTime.Now;
            return WebRequestExtensions.ExecuteXMLWebRequestTask<TimedOperation<string>>(
                string.Format(c_allCoversRequest, c_allCoversUser, md5String, imageSearchString),
                coversResult =>
                {
                    TimeSpan runTime = DateTime.Now - startTime;
                    try
                    {
                        IEnumerable<XElement> covers = from t in coversResult.Descendants("cover")
                                                       select t;
                        if (!covers.Any())
                        {
                            return new TimedOperation<string>(string.Empty, runTime);
                        }
                        else
                        {
                            XElement selectedRatingElement = covers.Select(p => p.Descendants("average_rating").First()).
                                Where(p => false == p.Value.Equals("n/a", StringComparison.OrdinalIgnoreCase)).
                                OrderByDescending(p => double.Parse(p.Value)).FirstOrDefault();

                            if (selectedRatingElement == default(XElement))
                            {
                                selectedRatingElement = covers.Descendants("average_rating").First();
                            }

                            string thumbnailSrc = selectedRatingElement.Parent.Descendants("thumbnail").First().Value;
                            return new TimedOperation<string>(thumbnailSrc, runTime);
                        }
                    }
                    catch
                    {
                        return new TimedOperation<string>(string.Empty, runTime);
                    }
                },
                request =>
                {
                    request.CachePolicy = new HttpRequestCachePolicy(HttpCacheAgeControl.MaxAge, TimeSpan.FromDays(1));
                });
        }

        #endregion
    }
}
